home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/env python
- '''
- $Id: pygtk-demo.py,v 1.8 2004/10/01 23:35:38 finlay Exp $
-
- pygtk-demo.py
- 2004-07-18: Some enhancements for building the demolist like in gtk-demos of the
- gtk+ distribution.
- 2004-07-22: Simple syntaxhighlighting implemented, based on the tokenizer-module.
- '''
-
- import string
- import re
- try:
- import pygtk
- pygtk.require('2.0')
- except ImportError:
- pass
- import gobject
- import gtk
- import pango
-
- # use for simple syntax highlighting ;-)
- import tokenize
- import keyword
-
- import demos
- D_TEMPL = '%sDemo'
-
- # Some programmatic definition for the testgtk_demos list. This avoids extra
- # maintenance if the demo list grows up. The current definition requires
- # a class or function with a swapped case name+'Demo' like in the doc string.
- # Swapped case is build from the __doc__-string programatically.
- child_demos = {}
- testgtk_demos = []
- for descr, mod in demos.demo_list:
- # Find some categorized demos
- try:
- main, child = descr.split('/')
- except ValueError:
- # No, only one application
- demo_class = D_TEMPL % re.sub('(\S+) *',
- lambda m:(m.group(1)[0].isupper() and m.group(1) or m.group(1).capitalize()),
- descr)
- testgtk_demos.append((descr, mod, demo_class))
- else:
- # Ok. Some more testing
- demo_class = D_TEMPL % re.sub('(\S+) *',
- lambda m:(m.group(1)[0].isupper() and m.group(1) or m.group(1).capitalize()),
- child)
- try:
- # Applicationgroup already defined?
- child_demos[main.upper()].append((child, mod, demo_class))
- except KeyError:
- # No. Start a new category
- child_demos.setdefault(main.upper(), []).append((child, mod, demo_class))
- testgtk_demos.append((main, None, None, child_demos[main.upper()]))
-
- (
- TITLE_COLUMN,
- MODULE_COLUMN,
- FUNC_COLUMN,
- ITALIC_COLUMN
- ) = range(4)
-
- CHILDREN_COLUMN = 3
-
- class InputStream(object):
- ''' Simple Wrapper for File-like objects. [c]StringIO doesn't provide
- a readline function for use with generate_tokens.
- Using a iterator-like interface doesn't succeed, because the readline
- function isn't used in such a context. (see <python-lib>/tokenize.py)
- '''
- def __init__(self, data):
- self.__data = [ '%s\n' % x for x in data.splitlines() ]
- self.__lcount = 0
- def readline(self):
- try:
- line = self.__data[self.__lcount]
- self.__lcount += 1
- except IndexError:
- line = ''
- self.__lcount = 0
- return line
-
-
- class PyGtkDemo(gtk.Window):
- info_buffer = None
- source_buffer = None
- module_cache = {}
-
- def __init__(self):
- gtk.Window.__init__(self)
- self.set_title("PyGTK+ Code Demos")
- self.connect('destroy', lambda w: gtk.main_quit())
- self.set_default_size(800, 400)
-
- hbox = gtk.HBox(False, 3)
- self.add(hbox)
-
- treeview = self.__create_treeview()
- hbox.pack_start(treeview, False, False)
-
- self.notebook = gtk.Notebook()
- hbox.pack_start(self.notebook, expand=True)
-
- scrolled_window, self.info_buffer = self.__create_text(False)
- self._new_notebook_page(scrolled_window, '_Info')
- tag = self.info_buffer.create_tag('title')
- tag.set_property('font', 'Sans 18')
-
- scrolled_window, self.source_buffer = self.__create_text(True)
- self._new_notebook_page(scrolled_window, '_Source')
- tag = self.source_buffer.create_tag('source')
- tag.set_property('font', 'monospace')
- tag.set_property('pixels_above_lines', 0)
- tag.set_property('pixels_below_lines', 0)
- tag = self.source_buffer.create_tag('keyword', foreground='#00007F',
- weight=pango.WEIGHT_BOLD)
- tag = self.source_buffer.create_tag('string', foreground='#7F007F')
- tag = self.source_buffer.create_tag('comment', foreground='#007F00',
- style=pango.STYLE_ITALIC)
-
- self.show_all()
-
- def run(self):
- gtk.main()
-
- def _new_notebook_page(self, widget, label):
- l = gtk.Label('')
- l.set_text_with_mnemonic(label)
- self.notebook.append_page(widget, l)
-
- def __create_treeview(self):
- model = gtk.TreeStore(
- gobject.TYPE_STRING,
- gobject.TYPE_STRING,
- gobject.TYPE_STRING,
- gobject.TYPE_BOOLEAN
- )
-
- treeview = gtk.TreeView(model)
- selection = treeview.get_selection()
- selection.set_mode(gtk.SELECTION_BROWSE)
- treeview.set_size_request(200, -1)
-
- for module in testgtk_demos:
- iter = model.append(None)
- model.set(iter,
- TITLE_COLUMN, module[TITLE_COLUMN],
- MODULE_COLUMN, module[MODULE_COLUMN],
- FUNC_COLUMN, module[FUNC_COLUMN],
- ITALIC_COLUMN, False
- )
-
- try:
- children = module[CHILDREN_COLUMN]
- for child_module in children:
- child_iter = model.append(iter)
- model.set(child_iter,
- TITLE_COLUMN, child_module[TITLE_COLUMN],
- MODULE_COLUMN, child_module[MODULE_COLUMN],
- FUNC_COLUMN, child_module[FUNC_COLUMN],
- ITALIC_COLUMN, False
- )
- except IndexError:
- pass
-
- cell = gtk.CellRendererText()
- cell.set_property('style', pango.STYLE_ITALIC)
-
- column = gtk.TreeViewColumn("Widget (double click for demo)", cell,
- text=TITLE_COLUMN, style_set=ITALIC_COLUMN)
-
- treeview.append_column(column)
-
- selection.connect('changed', self.selection_changed_cb)
- treeview.connect('row-activated', self.row_activated_cb)
-
- treeview.expand_all()
-
- return treeview
-
- def __create_text(self, is_source=False):
- scrolled_window = gtk.ScrolledWindow()
- scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- scrolled_window.set_shadow_type(gtk.SHADOW_IN)
-
- text_view = gtk.TextView()
- scrolled_window.add(text_view)
-
- buffer = gtk.TextBuffer(None)
- text_view.set_buffer(buffer)
- text_view.set_editable(False)
- text_view.set_cursor_visible(False)
-
- text_view.set_wrap_mode(not is_source)
-
- return scrolled_window, buffer
-
- def row_activated_cb(self, treeview, path, column):
- model = treeview.get_model()
- iter = model.get_iter(path)
- module_name = model.get_value(iter, MODULE_COLUMN)
- func_name = model.get_value(iter, FUNC_COLUMN)
- italic_value = model.get_value(iter, ITALIC_COLUMN)
- try:
- self.module_cache[module_name].present()
- except KeyError:
- module = getattr(demos, module_name)
- model.set(iter, ITALIC_COLUMN, not italic_value)
- cmd = 'demos.%s.%s' % (module_name, func_name)
- #print cmd
- window = eval(cmd)(self)
- if window:
- window.connect('destroy', self.window_closed_cb, model, path)
- self.module_cache[module_name] = window
-
- def selection_changed_cb(self, selection):
- model, iter = selection.get_selected()
- if not iter:
- return False
-
- name = model.get_value(iter, MODULE_COLUMN)
- self.load_module(name)
-
- def window_closed_cb (self, window, model, path):
- iter = model.get_iter(path)
- module_name = model.get_value(iter, MODULE_COLUMN)
- del self.module_cache[module_name]
- italic_value = model.get_value(iter, ITALIC_COLUMN)
- if italic_value:
- model.set(iter, ITALIC_COLUMN, not italic_value)
-
-
- def read_module(self, module):
- filename = module.__file__
- if filename[-4:] == '.pyc':
- filename = filename[:-1]
- fd = open(filename)
- return fd.read()
-
- def insert_documentation(self, module):
- buffer = self.info_buffer
- iter = buffer.get_iter_at_offset(0)
-
- lines = string.split(module.__doc__ or '', '\n')
- buffer.insert(iter, lines[0])
- start = buffer.get_iter_at_offset(0)
- buffer.apply_tag_by_name('title', start, iter)
- buffer.insert(iter, '\n')
- for line in lines[1:]:
- buffer.insert(iter, line)
- buffer.insert(iter, '\n')
-
- def clear_buffers(self):
- start, end = self.info_buffer.get_bounds()
- self.info_buffer.delete(start, end)
-
- start, end = self.source_buffer.get_bounds()
- self.source_buffer.delete(start, end)
-
- def insert_source(self, data):
- source_buffer = self.source_buffer
- iter = source_buffer.get_iter_at_offset(0)
-
- last_erow, last_ecol = 0, 0
- was_newline = False # multiline statement detection
- for x in tokenize.generate_tokens(InputStream(data).readline):
- # x has 5-tuples
- tok_type, tok_str = x[0], x[1]
- srow, scol = x[2]
- erow, ecol = x[3]
-
- # The tokenizer 'eats' the whitespaces, so we have to insert this again
- # if needed.
- if srow == last_erow:
- # Same line, spaces between statements
- if scol != last_ecol:
- source_buffer.insert_with_tags_by_name(iter, ' '*(scol-last_ecol), 'source')
- else:
- # New line.
- # First: Detect multiline statements. There is no special in the tokenizer stream.
- if was_newline is False and last_erow != 0:
- source_buffer.insert_with_tags_by_name(iter, ' \\\n', 'source')
- # new line check if it starts with col 0
- if scol != 0:
- source_buffer.insert_with_tags_by_name(iter, ' '*scol, 'source')
- last_erow = erow
- last_ecol = ecol
-
- if tok_type == tokenize.COMMENT:
- was_newline = True # newline is in tok_str included.
- source_buffer.insert_with_tags_by_name(iter, tok_str, 'source', 'comment')
- continue
- elif tok_type == tokenize.NAME:
- if tok_str in keyword.kwlist:
- source_buffer.insert_with_tags_by_name(iter, tok_str, 'source', 'keyword')
- continue
- elif tok_type == tokenize.STRING:
- source_buffer.insert_with_tags_by_name(iter, tok_str, 'source', 'string')
- continue
-
- # No special format for use. Check for newline.
- was_newline = tok_type in (tokenize.NEWLINE, tokenize.NL)
- source_buffer.insert_with_tags_by_name(iter, tok_str, 'source')
-
- def load_module(self, name):
- self.clear_buffers()
- if name is None: return
- module = getattr(demos, name)
- if module.__doc__:
- self.insert_documentation(module)
-
- source = self.read_module(module)
- self.insert_source(source)
-
- if __name__ == '__main__':
- PyGtkDemo().run()
-